package cn.sylinx.hbatis.ext.transaction;

import java.sql.Connection;
import java.sql.SQLException;

import cn.sylinx.hbatis.db.common.ITransaction;
import cn.sylinx.hbatis.exception.ErrorCodeRecordable;
import cn.sylinx.hbatis.exception.TransactionException;
import cn.sylinx.hbatis.ext.common.repository.CommonDaoService;
import cn.sylinx.hbatis.ext.ctxds.DynamicDatasourceProvider;
import cn.sylinx.hbatis.ext.starter.util.DaoHelper;
import cn.sylinx.hbatis.kit.Ret;
import cn.sylinx.hbatis.kit.StrKit;
import cn.sylinx.hbatis.log.GLog;

public abstract class AbstractTransactionalSupport {

	protected static interface Invoker {
		Object invoke() throws Throwable;
	}

	protected Object transaction(Transactional trans, Invoker invoker) {

		String truelyDatasource = trans.dynamic() ? DynamicDatasourceProvider.create().getDatasource()
				: trans.datasource();

		CommonDaoService daoService = StrKit.isBlank(truelyDatasource) ? DaoHelper.create()
				: DaoHelper.create(truelyDatasource);

		ITransaction transaction = new ITransaction() {

			@Override
			public Ret run() throws SQLException {

				Ret result = Ret.success();

				try {
					result.setData(invoker.invoke());
				} catch (Throwable e) {
					GLog.error("proceed error", e);
					String msg = StrKit.isBlank(e.getMessage()) || e.getMessage() == "null" ? "执行事务异常" : e.getMessage();

					int code = 500;
					if (ErrorCodeRecordable.class.isAssignableFrom(e.getClass())) {
						code = ((ErrorCodeRecordable) e).getCode();
					}

					return Ret.error(code, msg);
				}

				return result;
			}

			@Override
			public Integer transactionIsolation() {

				int ti = trans.transactionIsolation();
				if (ti == Connection.TRANSACTION_NONE || ti == Connection.TRANSACTION_READ_UNCOMMITTED
						|| ti == Connection.TRANSACTION_READ_COMMITTED || ti == Connection.TRANSACTION_REPEATABLE_READ
						|| ti == Connection.TRANSACTION_SERIALIZABLE) {
					return ti;
				}

				return ITransaction.super.transactionIsolation();
			}

		};

		Ret r = daoService.transactionWithReturn(transaction);

		if (r.isSuccess()) {
			return r.getData();
		}

		throw new TransactionException(r.getCode(), r.getError());
	}
}
